package com.sh.data.engine.application.service.normalization;

import com.sh.data.engine.application.model.dto.base.PagerResponse;
import com.sh.data.engine.application.model.dto.dataasset.TableInfoBizDTO;
import com.sh.data.engine.application.model.dto.integration.PreviewDataDTO;
import com.sh.data.engine.application.model.dto.normalization.index.AssociatedTaskResponse;
import com.sh.data.engine.application.model.dto.normalization.index.IndexCatalogResponse;
import com.sh.data.engine.application.model.dto.normalization.index.IndexResponse;
import com.sh.data.engine.application.model.mapper.datadev.TableInfoBizDTOMapper;
import com.sh.data.engine.application.model.mapper.normalization.AssociatedTaskResponseMapper;
import com.sh.data.engine.application.model.mapper.normalization.IndexResponseMapper;
import com.sh.data.engine.application.model.mapper.normalization.IndexResponsePageMapper;
import com.sh.data.engine.application.model.request.normalization.*;
import com.sh.data.engine.common.enumDefinition.DSType;
import com.sh.data.engine.common.exception.BusinessException;
import com.sh.data.engine.common.util.ServletUtils;
import com.sh.data.engine.common.util.TableNameUtil;
import com.sh.data.engine.domain.base.model.PageResult;
import com.sh.data.engine.domain.base.model.UserContext;
import com.sh.data.engine.domain.common.service.TableManager;
import com.sh.data.engine.domain.dataasset.model.domain.TableInfoBizDomain;
import com.sh.data.engine.domain.dataasset.service.TblPropService;
import com.sh.data.engine.domain.datadev.offline.model.domain.OfflineDevDirTreeDomain;
import com.sh.data.engine.domain.datadev.offline.model.enums.OfflineDevTopNodeIdEnum;
import com.sh.data.engine.domain.datadev.offline.model.param.OfflineDevSaveTaskFlowParam;
import com.sh.data.engine.domain.datadev.offline.service.OfflineDevMenuNodeService;
import com.sh.data.engine.domain.normalization.index.model.domain.AssociatedTaskDomain;
import com.sh.data.engine.domain.normalization.index.model.domain.IndexCatalogDomain;
import com.sh.data.engine.domain.normalization.index.model.domain.IndexDomain;
import com.sh.data.engine.domain.normalization.index.model.param.IndexListParam;
import com.sh.data.engine.domain.normalization.index.model.param.IndexParam;
import com.sh.data.engine.domain.normalization.index.model.param.PreviewDataParam;
import com.sh.data.engine.domain.normalization.index.service.IndexMgtService;
import com.sh.data.engine.domain.shims.db.model.FieldInfoDomain;
import com.sh.data.engine.domain.util.ConvertUtil;
import com.sh.data.engine.domain.workspace.manager.service.impl.StorageServiceImpl;
import com.sh.data.engine.repository.dao.datadev.entity.DatadevMenuNodeEntity;
import com.sh.data.engine.repository.dao.normalization.index.entity.IndexEntity;
import com.sh.data.engine.repository.dao.workspace.manager.entity.StorageEntity;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author: mengzheng.mhc
 * @date: 2024/7/4 13:32
 */
@Slf4j
@Service
public class IndexMgtApplicationService {

    @Autowired
    private IndexMgtService indexMgtService;

    @Autowired
    private TblPropService tblPropService;

    @Autowired
    private StorageServiceImpl storageServiceImpl;

    @Autowired
    private TableManager tableManager;

    @Autowired
    private IndexResponsePageMapper indexResponsePageMapper;

    @Autowired
    private IndexResponseMapper indexResponseMapper;

    @Autowired
    private TableInfoBizDTOMapper tableInfoBizDTOMapper;

    @Autowired
    private AssociatedTaskResponseMapper associatedTaskResponseMapper;

    @Autowired
    private OfflineDevMenuNodeService offlineDevMenuNodeService;

    public List<IndexCatalogResponse> listCatalog() {
        Long projectId = ServletUtils.getProjectId();
        List<IndexCatalogDomain> indexCatalogDomains = indexMgtService.listCatalog(projectId);
        return ConvertUtil.copyProperties(indexCatalogDomains, IndexCatalogResponse.class);
    }

    public List<IndexCatalogResponse> listCatalogWithTree() {
        Long projectId = ServletUtils.getProjectId();
        List<IndexCatalogDomain> indexCatalogDomains = indexMgtService.listCatalogWithTree(projectId);
        return ConvertUtil.copyProperties(indexCatalogDomains, IndexCatalogResponse.class);
    }

    public boolean addCatalog(String name, Long pid) {
        Long projectId = ServletUtils.getProjectId();
        indexMgtService.addCatalog(name, projectId, pid);
        return true;
    }

    public boolean editCatalog(String name, Long id) {
        if (id == 0) {
            throw new BusinessException("禁止修改全部分组");
        }

        return indexMgtService.editCatalog(name, id);
    }

    public boolean deleteCatalog(Long id) {
        if (id == 0) {
            throw new BusinessException("禁止修改全部分组");
        }

        return indexMgtService.delCatalog(id);
    }

    public boolean addIndex(IndexRequest indexRequest) {
        Long projectId = ServletUtils.getProjectId();
        IndexParam indexParam = ConvertUtil.copyProperties(indexRequest, IndexParam.class);
        indexParam.setProjectId(projectId);
        return indexMgtService.addIndex(indexParam);
    }

    public boolean addIndexVersion(IndexRequest indexRequest) {
        Long projectId = ServletUtils.getProjectId();
        IndexParam indexParam = ConvertUtil.copyProperties(indexRequest, IndexParam.class);
        indexParam.setProjectId(projectId);
        return indexMgtService.addIndexVersion(indexParam);
    }

    public PagerResponse<IndexResponse> indexList(IndexListRequest indexListRequest) {
        Long projectId = ServletUtils.getProjectId();
        Long catalogId = indexListRequest.getCatalogId();

        if (catalogId != null && catalogId == 0L) {
            catalogId = null;
        }
        List<Long> catalogIdList = null;
        // 获取某个目录下的子目录
        if (catalogId != null) {
            catalogIdList = indexMgtService.getAllChildIdByPid(catalogId, projectId);
        }
        IndexListParam indexListParam =
            ConvertUtil.copyProperties(indexListRequest, IndexListParam.class);
        indexListParam.setProjectId(projectId);
        indexListParam.setCatalogIdList(catalogIdList);

        PageResult<IndexDomain> indexDomainPageResult = indexMgtService.indexList(indexListParam);
        return indexResponsePageMapper.toPageResponse(indexDomainPageResult);
    }

    public IndexResponse indexDetail(Long id) {
        IndexDomain indexDomain = indexMgtService.indexDetail(id);
        return indexResponseMapper.map(indexDomain);
    }

    public boolean deleteIndex(Long id) {
        return indexMgtService.delIndex(id);
    }

    public List<TableInfoBizDTO> getTables() {
        Long projectId = ServletUtils.getProjectId();
        List<TableInfoBizDomain> tableList = indexMgtService.getTableList(projectId);
        return tableInfoBizDTOMapper.map(tableList);
    }

    public PreviewDataDTO getData(String targetTblName) {
        if (StringUtils.isBlank(targetTblName)) {
            throw new BusinessException("表名不能为空");
        }
        Long projectId = ServletUtils.getProjectId();
        boolean secretLevel = tblPropService.checkSecretLevel(targetTblName,
            projectId, UserContext.getUserId());
        List<Map<String, Object>> data = tableManager.getDataLimit10(targetTblName, projectId);
        PreviewDataDTO previewDataDTO = new PreviewDataDTO();

        // 列名集合  需要拼接描述
        List<FieldInfoDomain> tableField = tableManager.getTableField(projectId, targetTblName);
        List<String> collect =
            tableField.stream()
                .map(
                    (tf) -> {
                        if (StringUtils.isNotBlank(tf.getComment())) {
                            return tf.getFieldName() + "(" + tf.getComment() + ")";
                        }
                        return tf.getFieldName();
                    })
                .collect(Collectors.toList());
        previewDataDTO.setColumns(collect);

        if (data == null || data.size() == 0) {
            // 该表没有数据，只返回字段
            previewDataDTO.setRows(null);
            return previewDataDTO;
        }

        // 行记录集合
        List<List<Object>> rows = new ArrayList<>();
        for (Map<String, Object> datum : data) {
            Collection<Object> values = datum.values();
            List<Object> row = new ArrayList<>(values);
            rows.add(row);
        }
        previewDataDTO.setRows(rows);
        return previewDataDTO;
    }

    public PreviewDataDTO filterSort(FilterSortRequest filterSortRequest) {
        Long projectId = ServletUtils.getProjectId();
        String targetTblName = filterSortRequest.getTargetTblName();

        final StorageEntity storageEntity = storageServiceImpl.getStorageInfoByProjectId(projectId);
        String storageType = storageEntity.getStorageType();

        String sql = createSql(filterSortRequest, storageType);

        log.info("指标管理查询的sql: " + sql);

        List<Map<String, Object>> data = tableManager.getDataBySql(targetTblName, projectId, sql);
        PreviewDataDTO previewDataDTO = new PreviewDataDTO();
        if (data == null || data.size() == 0) {
            // 该表没有数据，只返回字段
            List<FieldInfoDomain> tableField = tableManager.getTableField(projectId, targetTblName);
            List<String> collect =
                tableField.stream().map(tf -> tf.getFieldName()).collect(Collectors.toList());
            previewDataDTO.setColumns(collect);
            previewDataDTO.setRows(null);
            return previewDataDTO;
        }
        // 列名集合
        previewDataDTO.setColumns(new ArrayList<>(data.get(0).keySet()));
        // 行记录集合
        List<List<Object>> rows = new ArrayList<>();
        for (Map<String, Object> datum : data) {
            Collection<Object> values = datum.values();
            List<Object> row = new ArrayList<>(values);
            rows.add(row);
        }
        previewDataDTO.setRows(rows);
        return previewDataDTO;
    }

    public PreviewDataDTO getDataAll(String targetTblName) {
        if (StringUtils.isBlank(targetTblName)) {
            throw new BusinessException("表名不能为空");
        }
        String sql;
        Long projectId = ServletUtils.getProjectId();
        final StorageEntity storageEntity = storageServiceImpl.getStorageInfoByProjectId(projectId);
        String storageType = storageEntity.getStorageType();
        if (storageType.equals(DSType.Sap.name()) || storageType.equals(DSType.PostgreSQL.name())) {
            // 如果是sap或者pg 要拼接上schema
            sql = "select * from " + TableNameUtil.getTblNameLong(targetTblName);
        } else {
            // 如果是其他 hive tidb 表名不要加入引号
            sql = "select * from " + targetTblName;
        }
        log.info("指标管理sql:" + sql);
        List<Map<String, Object>> data = tableManager.getDataBySql(targetTblName, projectId, sql);
        PreviewDataDTO previewDataDTO = new PreviewDataDTO();
        if (data == null || data.size() == 0) {
            // 该表没有数据，只返回字段
            List<FieldInfoDomain> tableField = tableManager.getTableField(projectId, targetTblName);
            List<String> collect =
                tableField.stream().map(tf -> tf.getFieldName()).collect(Collectors.toList());
            previewDataDTO.setColumns(collect);
            previewDataDTO.setRows(null);
            return previewDataDTO;
        }
        // 列名集合
        previewDataDTO.setColumns(new ArrayList<>(data.get(0).keySet()));
        // 行记录集合
        List<List<Object>> rows = new ArrayList<>();
        for (Map<String, Object> datum : data) {
            Collection<Object> values = datum.values();
            List<Object> row = new ArrayList<>(values);
            rows.add(row);
        }
        previewDataDTO.setRows(rows);
        return previewDataDTO;
    }

    public Map<String, Set<String>> getElement(String targetTblName) {
        // 用于封装查询某个字段的不重复元素
        List<Map<String, Object>> dataResult;

        // 首先查询出所有数据
        if (StringUtils.isBlank(targetTblName)) {
            throw new BusinessException("表名不能为空");
        }
        String sql;
        Long projectId = ServletUtils.getProjectId();
        final StorageEntity storageEntity = storageServiceImpl.getStorageInfoByProjectId(projectId);
        String storageType = storageEntity.getStorageType();
        if (storageType.equals(DSType.Sap.name())
            || storageType.equals(DSType.PostgreSQL.name())
            || storageType.equals(DSType.MatrixDB.name()) || storageType.equals(DSType.KINGBASE8.name())) {
            // 如果是sap或者pg 要拼接上schema
            sql = "select * from " + TableNameUtil.getTblNameLong(targetTblName);
        } else {
            // 如果是其他 hive tidb 表名不要加入引号
            sql = "select * from " + targetTblName;
        }
        log.info("指标管理sql:" + sql);
        List<Map<String, Object>> data = tableManager.getDataBySql(targetTblName, projectId, sql);

        dataResult = data;

        Map<String, Set<String>> result = new HashMap<>();

        if (CollectionUtils.isEmpty(dataResult)) {
            // throw new BusinessException(StatusCode.FAILED, "请先查询出所有数据");
            return result;
        }
        // 获取列名集合
        Set<String> keySet = dataResult.get(0).keySet();
        for (String key : keySet) {

            Set<String> notRepeatElements = new HashSet<>();
            // 获取每一列的不重复字段
            if (CollectionUtils.isEmpty(dataResult)) {
                return null;
            }

            for (Map<String, Object> datum : dataResult) {
                Object column = datum.get(key);
                if (column != null) {
                    notRepeatElements.add(column.toString());
                }
            }

            result.put(key, notRepeatElements);
        }

        return result;
    }

    public void indexListExport(HttpServletResponse response, String targetTblName, Long projectId) throws IOException {
        PreviewDataDTO previewDataDTO = getDataAllForExcel(targetTblName, projectId);
        PreviewDataParam previewDataParam =
            ConvertUtil.copyProperties(previewDataDTO, PreviewDataParam.class);

        if (previewDataParam == null) {
            throw new BusinessException("结果数据结果为空，无法导出");
        }
        indexMgtService.indexListExport(response, previewDataParam);
    }

    public boolean saveIndexCorrelation(IndexCorrelationExtenalRequest indexCorrelationRequest) {

        var indexCorrelation = indexCorrelationRequest.getIndexCorrelation();
        return indexMgtService.saveIndexCorrelation(
            indexCorrelationRequest.getId(),
            indexCorrelation.getTblName(),
            indexCorrelation.getTblId());
    }

    public List<AssociatedTaskResponse> getAssociatedTasks(String tableName) {
        Long projectId = ServletUtils.getProjectId();
        List<AssociatedTaskDomain> associatedTasks =
            indexMgtService.getAssociatedTasks(tableName, projectId);
        return associatedTaskResponseMapper.map(associatedTasks);
    }

    public List<OfflineDevDirTreeDomain> getOfflineTree() {
        Long projectId = ServletUtils.getProjectId();

        // Id固定传"2"
        return offlineDevMenuNodeService.getDirTree(
            Long.valueOf(OfflineDevTopNodeIdEnum.TASK_FLOW.getId()), projectId);
    }

    public boolean saveTaskFlow(IndexOfflineDevTaskFlowSaveRequest request) {
        String userId = UserContext.getUserId();
        Long projectId = ServletUtils.getProjectId();

        OfflineDevSaveTaskFlowParam param = new OfflineDevSaveTaskFlowParam();
        param.setPid(request.getPid());
        param.setName(request.getName());
        param.setRemark(request.getRemark());
        param.setProjectId(projectId);
        param.setUserId(userId);

        DatadevMenuNodeEntity datadevMenuNodeEntity = offlineDevMenuNodeService.saveTaskFlow(param);
        boolean save = Objects.nonNull(datadevMenuNodeEntity);
        // 保存成功后把NodeId存到指标的记录中去
        if (save) {
            Long idByNamePid =
                offlineDevMenuNodeService.getIdByNamePid(request.getName(), request.getPid());
            IndexEntity indexEntity = new IndexEntity();
            indexEntity.setId(request.getIndexId());
            indexEntity.setNodeId(idByNamePid);
            indexMgtService.updateByIdSelective(indexEntity);
        }

        return save;
    }

    /**
     * @description : projectId 前端传入
     * @author : tinglan.ys
     * @date : 2022/6/2
     */
    private PreviewDataDTO getDataAllForExcel(String targetTblName, Long projectId) {
        if (StringUtils.isBlank(targetTblName)) {
            throw new BusinessException("表名不能为空");
        }
        String sql;
        final StorageEntity storageEntity = storageServiceImpl.getStorageInfoByProjectId(projectId);
        String storageType = storageEntity.getStorageType();
        if (storageType.equals(DSType.Sap.name()) || storageType.equals(DSType.PostgreSQL.name())) {
            // 如果是sap或者pg 要拼接上schema
            sql = "select * from " + TableNameUtil.getTblNameLong(targetTblName);
        } else {
            // 如果是其他 hive tidb 表名不要加入引号
            sql = "select * from " + targetTblName;
        }
        log.info("指标管理sql:" + sql);
        List<Map<String, Object>> data = tableManager.getDataBySql(targetTblName, projectId, sql);
        PreviewDataDTO previewDataDTO = new PreviewDataDTO();
        if (data == null || data.size() == 0) {
            // 该表没有数据，只返回字段
            List<FieldInfoDomain> tableField = tableManager.getTableField(projectId, targetTblName);
            List<String> collect =
                tableField.stream().map(tf -> tf.getFieldName()).collect(Collectors.toList());
            previewDataDTO.setColumns(collect);
            previewDataDTO.setRows(null);
            return previewDataDTO;
        }
        // 列名集合
        previewDataDTO.setColumns(new ArrayList<>(data.get(0).keySet()));
        // 行记录集合
        List<List<Object>> rows = new ArrayList<>();
        for (Map<String, Object> datum : data) {
            Collection<Object> values = datum.values();
            List<Object> row = new ArrayList<>(values);
            rows.add(row);
        }
        previewDataDTO.setRows(rows);
        return previewDataDTO;
    }

    /**
     * @description : 根据查询条件拼接sql
     * @author : tinglan.ys
     * @date : 2022/4/28
     */
    private static String createSql(FilterSortRequest filterSortRequest, String storageType) {

        String targetTblName = filterSortRequest.getTargetTblName();

        List<FilterSortRequest.FilterCondition> filterConditions =
            filterSortRequest.getFilterConditions();
        StringBuilder newSql = new StringBuilder();
        if (CollectionUtils.isEmpty(filterSortRequest.getFilterConditions())) {
            // 无查询条件 查询全部
            if (storageType.equals(DSType.Sap.name())
                || storageType.equals(DSType.PostgreSQL.name())
                || storageType.equals(DSType.MatrixDB.name()) || storageType.equals(DSType.KINGBASE8.name())) {
                // 如果是sap,pg 表名要拼接上schema
                newSql.append("select * from ").append(TableNameUtil.getTblNameLong(targetTblName));
            } else {
                // 如果是其他 表名不要加入引号
                newSql.append("select * from ").append(targetTblName);
            }

        } else {
            // 有查询条件 拼接sql
            // 首先对whereName两边加上'' 然后对字段按情况加 "" '' 或者不加

            if (storageType.equals(DSType.Sap.name())
                || storageType.equals(DSType.PostgreSQL.name())
                || storageType.equals(DSType.MatrixDB.name()) || storageType.equals(DSType.KINGBASE8.name())) {
                // 如果是sap,columnName两边加"" 如果是pg,columnName两边加""
                for (FilterSortRequest.FilterCondition filterCondition : filterConditions) {
                    String columnName = filterCondition.getColumnName();
                    filterCondition.setColumnName("\"" + columnName + "\"");
                    List<String> newWhereName = new ArrayList<>();
                    for (String whereName : filterCondition.getWhereName()) {
                        newWhereName.add("'" + whereName + "'");
                    }
                    filterCondition.setWhereName(newWhereName);
                }
            } else {
                // 其他不加
                for (FilterSortRequest.FilterCondition filterCondition : filterConditions) {
                    List<String> newWhereName = new ArrayList<>();
                    for (String whereName : filterCondition.getWhereName()) {
                        newWhereName.add("'" + whereName + "'");
                    }
                    filterCondition.setWhereName(newWhereName);
                }
            }

            if (storageType.equals(DSType.Sap.name()) || storageType.equals(DSType.PostgreSQL.name())) {
                newSql.append("select * from ").append(TableNameUtil.getTblNameLong(targetTblName));
            } else {
                newSql.append("select * from ").append(targetTblName);
            }
            if (filterConditions.size() == 1) {
                // where
                newSql
                    .append(" where ")
                    .append(filterConditions.get(0).getColumnName())
                    .append(" in( ")
                    .append(String.join(",", filterConditions.get(0).getWhereName()))
                    .append(" )");
            } else {
                // where and
                newSql.append(" where ");
                for (FilterSortRequest.FilterCondition filterCondition : filterConditions) {
                    //
                    newSql
                        .append(filterCondition.getColumnName())
                        .append(" in(")
                        .append(String.join(",", filterCondition.getWhereName()));
                    newSql.append(") and ");
                }
                // 删除多加的and
                newSql.deleteCharAt(newSql.length() - 1);
                newSql.deleteCharAt(newSql.length() - 1);
                newSql.deleteCharAt(newSql.length() - 1);
                newSql.deleteCharAt(newSql.length() - 1);
            }
        }
        // 拼接orderby
        if (StringUtils.isBlank(filterSortRequest.getOrderByColName())
            || StringUtils.isBlank(filterSortRequest.getOrder())) {

        } else {

            if (storageType.equals(DSType.Sap.name()) || storageType.equals(DSType.PostgreSQL.name())) {
                String orderByColName = filterSortRequest.getOrderByColName();
                filterSortRequest.setOrderByColName("\"" + orderByColName + "\"");
            }

            newSql.append(
                " order by "
                    + filterSortRequest.getOrderByColName()
                    + " "
                    + filterSortRequest.getOrder());
        }
        return newSql.toString();
    }
}
